

function string.utf8CharSize(c)
    if not c then return 0 end
    if c > 240 then return 4 end
    if c > 225 then return 3 end
    if c > 192 then return 2 end
    return 1
end

function string.utf8StringLen(str)
    if nil == str or "" == str then return 0 end
    assert("string" == type(str), "not string")
    
    local len = 0
    local i = 1
    while i <= #str do
        local b = string.byte(str, i)
        i = i + string.utf8CharSize(b)
        len = len + 1
    end
    return len
end

function string.utf8StringSub(str, index, count)
    if nil == index then index = 1 end
    if nil == count then count = string.utf8StringLen(str) end
    
    local byteCount = #str
    local i = 1
    --跳过起始的n个
    while index > 1 and i <= byteCount do
        local b = string.byte(str, i)
        i = i + string.utf8CharSize(b)
        index = index - 1
    end
    
    local j = i
    --子串结束位置
    while count > 0 and j <= byteCount do
        local b = string.byte(str, j)
        j = j + string.utf8CharSize(b)
        count = count - 1
    end
    
    return str:sub(i, j-1)
end

function string.utf8CharArray(str, index, count, dstTb, dstIndex)
    if nil == index then index = 1 end
    if nil == count then count = string.utf8StringLen(str) end
    if nil == dstTb then dstTb = {} end
    
    local byteCount = #str
    local i = 1
    --跳过起始的n个
    while index > 1 and i <= byteCount do
        local b = string.byte(str, i)
        i = i + string.utf8CharSize(b)
        index = index - 1
    end
    
    local j = i
    local leftCount = count
        --子串结束位置
        if nil == dstIndex then
            while leftCount > 0 and j <= byteCount do
            local b = string.byte(str, j)
            local size = string.utf8CharSize(b)
            local utf8Char = str:sub(j, j+size-1)
            table.insert(dstTb, utf8Char)
            j = j + size
            leftCount = leftCount - 1
        end
    else
        while leftCount > 0 and j <= byteCount do
            local b = string.byte(str, j)
            local size = string.utf8CharSize(b)
            local utf8Char = str:sub(j, j+size-1)
            dstTb[dstIndex] = utf8Char
            j = j + size
            leftCount = leftCount - 1
            dstIndex = dstIndex + 1
        end
    end
    
    return dstTb, count
end


local function Test1()
    assert(6 == string.utf8StringLen("123abc"))
    assert(2 == string.utf8StringLen("中文"))
    assert(4 == string.utf8StringLen("中文1a"))
    assert(5 == string.utf8StringLen(".,;<>"))
    assert(5 == string.utf8StringLen("。，；《》"))
    assert(10 == string.utf8StringLen(".,;<>。，；《》"))
    
    assert("文1" == string.utf8StringSub("中文1a", 2, 2))
    assert("文啊" == string.utf8StringSub("中文啊呀", 2, 2))
    
    local tb = string.utf8CharArray("中文啊呀", 2, 2)
    assert("文" == tb[1])
    assert("啊" == tb[2])
end

Test1()